/*
 * MIT License
 *
 * Copyright (c) 2024-2025 milkpotatoes
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

export class UniversalKVStroe<T> {
    static valueIterator<T>(data: ArrayLike<any>, key: number): Iterable<T> {
        let index = 0;
        const size = data.length;
        return {
            [Symbol.iterator]() {
                return {
                    next(): IteratorResult<T> {
                        if (index < size) {
                            return { value: data[index++][key], done: false };
                        } else {
                            return { value: undefined, done: true }
                        }
                    }
                }
            }
        }
    }

    static pairIterator<T>(data: ArrayLike<any>): Iterable<T> {
        let index = 0;
        const size = data.length;
        return {
            [Symbol.iterator]() {
                return {
                    next(): IteratorResult<T> {
                        if (index < size) {
                            return { value: data[index++], done: false };
                        } else {
                            return { value: undefined, done: true }
                        }
                    }
                }
            }
        }
    }

    data: Array<[string, T]> = [];

    get size(): number {
        return this.data.length;
    };
    append(key: string, value: T): void {
        this.data.push([key, value]);
    };

    delete(key: string, value?: string): void {
        for (let i = this.data.length - 1; i >= 0; i--) {
            if (this.data[i][0] === key) {
                if (value === undefined || this.data[i][1] === value)
                    this.data.splice(i, 1);
            }
        }
    };

    entries(): Iterable<[string, T]> {
        return UniversalKVStroe.pairIterator<[string, T]>(this.data);
    };

    forEach<K>(callback: ForEachCallback<K, T>, that: K, thisArg?: Object): void {
        let cb = callback.bind(thisArg ?? globalThis);
        this.data.forEach((v, i, a) => {
            cb(v[1], v[0], that);
        });
    };

    get(key: string): T | null {
        for (let i = 0; i < this.data.length; i++) {
            const pair = this.data[i];
            if (pair[0] === key) {
                return pair[1];
            }
        }
        return null;
    };

    getAll(key: string): T[] {
        const result: T[] = [];
        for (let i = 0; i < this.data.length; i++) {
            const pair = this.data[i];
            if (pair[0] === key) {
                result.push(pair[1]);
            }
        }
        return result;
    };

    has(key: string): boolean {
        return this.get(key) !== null;
    };

    keys(): Iterable<T> {
        return UniversalKVStroe.valueIterator<T>(this.data, 0);
    };

    set(key: string, value: T): void {
        this.delete(key);
        this.append(key, value);
    };

    sort(): void {
        this.data.sort((a, b) => {
            return a[0].localeCompare(b[0]);
        });
    }

    values(): Iterable<T> {
        return UniversalKVStroe.valueIterator<T>(this.data, 1);
    };

    [Symbol.iterator](): Iterator<[string, T]> {
        return UniversalKVStroe.pairIterator<[string, T]>(this.data)[Symbol.iterator]();
    };

}
